package io.modifier.basic.ext

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// 自定义异常，表示内部出错了
class InnerException(message: String = "inner value error") : Exception(message)

//<editor-fold desc="一对一">

// uploadFile()
//    .andThen { updateProfile(it) }
//    .onSuccess { updateProfileUI() }
//    .onFailure { showErrorPage() }

@OptIn(ExperimentalContracts::class)
inline fun <V, E> Result<V>.andThen(transform: (V) -> Result<E>): Result<E> {
    // kotlin 约定，告诉编译器 transform 最多执行一次
    contract {
        callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
    }
    if (isSuccess) {
        val value = getOrNull() ?: return Result.failure(InnerException())
        return transform(value)
    } else {
        val exception = exceptionOrNull() ?: return Result.failure(InnerException())
        return Result.failure(exception)
    }
}

//</editor-fold>


//<editor-fold desc="一对多">

// getUserPhoneNumber().dispatch { phoneNumber ->
//    val bilbilVideoPlayNum = getBilbilVideoPlayNum(phoneNumber).getOrThrow()
//    val tiktokVideoPlayNum = getTiktokVideoPlayNum(phoneNumber).getOrThrow()
//    bilbilVideoPlayNum + tiktokVideoPlayNum
//}.onFailure { println("onFailure $it") }
// .onSuccess { println("onSuccess $it") }

@OptIn(ExperimentalContracts::class)
inline fun <V, E> Result<V>.dispatch(transform: (V) -> E): Result<E> {
    contract {
        callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
    }

    if (isSuccess) {
        val value = getOrNull() ?: return Result.failure(InnerException())
        return kotlin.runCatching {
            transform(value)
        }
    } else {
        val exception = exceptionOrNull() ?: return Result.failure(InnerException())
        return Result.failure(exception)
    }
}

//</editor-fold>


//<editor-fold desc="多对一">

// zip {
//    val userInfo = getUserInfo().getOrThrow()
//    val invoiceNumber = getInvoiceNumber().getOrThrow()
//    userInfo to invoiceNumber
//}.andThen {
//    reimbursement(it.first, it.second)
//}.onFailure { println("onFailure $it") }
//    .onSuccess { println("onSuccess $it") }

@OptIn(ExperimentalContracts::class)
inline fun <V> zip(block: () -> V): Result<V> {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return runCatching {
        block()
    }
}

//</editor-fold>


//<editor-fold desc="选择">

// payByChange().or(payByHuabei())
//    .onFailure { println("onFailure $it") }
//    .onSuccess { println("onSuccess $it") }

fun <V> Result<V>.or(result: Result<V>): Result<V> {
    return when {
        isSuccess -> this
        else -> result
    }
}

//</editor-fold>


//<editor-fold desc="集合">

// 获取成功结果的列表
fun <V, R : Result<V>> valuesOf(results: List<R>): List<V> {
    return results.asIterable().filterValues()
}

fun <V> Iterable<Result<V>>.filterValues(): List<V> {
    return filterValuesTo(ArrayList())
}

fun <V, C : MutableCollection<in V>> Iterable<Result<V>>.filterValuesTo(destination: C): C {
    for (element in this) {
        if (element.isSuccess) {
            val value = element.getOrNull() ?: continue
            destination.add(value)
        }
    }
    return destination
}

fun <V> Iterable<Result<V>>.allSuccess(): Boolean {
    return all(Result<V>::isSuccess)
}

//</editor-fold>


//<editor-fold desc="协程">

// getUserPhoneNumber().coroutineDispatch { phoneNumber ->
//    val bilbilVideoPlayNum = async {
//        getBilbilVideoPlayNum(phoneNumber).bind()
//    }
//    val tiktokVideoPlayNum = async {
//        getTiktokVideoPlayNum(phoneNumber).bind()
//    }
//    bilbilVideoPlayNum.await() + tiktokVideoPlayNum.await()
//}.onFailure { println("onFailure $it") }
// .onSuccess { println("onSuccess $it") }

// 由于 async 需要一个 CoroutineScope 来启动，这里创建一个Result的CoroutineScope
interface CoroutineResultScope<V> : CoroutineScope {
    suspend fun <V> Result<V>.bind(): V
}

class CoroutineResultScopeImpl<V>(
    delegate: CoroutineScope,
) : CoroutineResultScope<V>, CoroutineScope by delegate {

    private val mutex = Mutex()
    var result: Result<V>? = null

    // 使用 bind 支持协程的结构化编程，这样当一个协程任务异常失败时，
    // 取消其他的协程任务
    override suspend fun <V> Result<V>.bind(): V {
        return if (isSuccess) {
            getOrNull() ?: throw InnerException()
        } else {
            mutex.withLock {
                if (result == null) {
                    result = Result.failure(this.exceptionOrNull() ?: InnerException())
                    coroutineContext.cancel()
                }
                throw CancellationException()
            }
        }
    }
}

@OptIn(ExperimentalContracts::class)
suspend inline fun <V, E> Result<V>.coroutineDispatch(crossinline block: suspend CoroutineResultScope<E>.(V) -> E): Result<E> {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    if (isSuccess) {
        lateinit var receiver: CoroutineResultScopeImpl<E>
        return try {
            coroutineScope {
                receiver = CoroutineResultScopeImpl(this)
                val value = getOrNull() ?: throw InnerException()
                with(receiver) {
                    Result.success(block(value))
                }
            }
        } catch (ex: CancellationException) {
            receiver.result!!
        }
    } else {
        return Result.failure(exceptionOrNull() ?: InnerException())
    }
}

//</editor-fold>


